package cn.bustack.middleware.dynamic.thread.pool.sdk.config;

import cn.bustack.middleware.dynamic.thread.pool.sdk.domain.DynamicThreadPoolSercive;
import cn.bustack.middleware.dynamic.thread.pool.sdk.domain.IDynamicThreadPoolService;
import cn.bustack.middleware.dynamic.thread.pool.sdk.domain.model.entity.ThreadPoolConfigEntity;
import cn.bustack.middleware.dynamic.thread.pool.sdk.domain.model.valobj.RegistryEnumVO;
import cn.bustack.middleware.dynamic.thread.pool.sdk.registry.IRegistry;
import cn.bustack.middleware.dynamic.thread.pool.sdk.registry.redis.RedisRegistry;
import cn.bustack.middleware.dynamic.thread.pool.sdk.trigger.job.ThreadPoolDataReportJob;
import cn.bustack.middleware.dynamic.thread.pool.sdk.trigger.listener.ThreadPoolConfigAdjustListener;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

/*
* 动态配置
* */
@Configuration
@EnableConfigurationProperties(DynamicThreadPoolAutoProperties.class)
@EnableScheduling//这个注解让任务执行
public class DynamicThreadPoolAutoConfig {

    //警告日志
    private final Logger logger = LoggerFactory.getLogger(DynamicThreadPoolAutoConfig.class);

    String applicationName;

    /*
    * RedissonClient的bean
    * */
    @Bean("dynamicThreadRedissonClient")
    public RedissonClient redissonClient(DynamicThreadPoolAutoProperties properties) {
        Config config = new Config();
        // 编解码器
        config.setCodec(JsonJacksonCodec.INSTANCE);

        config.useSingleServer()
                .setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
                .setPassword(properties.getPassword())
                .setConnectionPoolSize(properties.getPoolSize())
                .setConnectionMinimumIdleSize(properties.getMinIdleSize())
                .setIdleConnectionTimeout(properties.getIdleTimeout())
                .setConnectTimeout(properties.getConnectTimeout())
                .setRetryAttempts(properties.getRetryAttempts())
                .setRetryInterval(properties.getRetryInterval())
                .setPingConnectionInterval(properties.getPingInterval())
                .setKeepAlive(properties.isKeepAlive())
        ;

        RedissonClient redissonClient = Redisson.create(config);

        logger.info("动态线程池，注册器（redis）链接初始化完成。{} {} {}", properties.getHost(), properties.getPoolSize(), !redissonClient.isShutdown());

        return redissonClient;
    }

    /*
    * 注册中心的实例化对象
    * */
    @Bean
    public IRegistry redisRegistry(RedissonClient redissonClient){
        return new RedisRegistry(redissonClient);//redissonClient在上面已经实例化好了，这里可以自动注入进来
    }

    /*
    * 线程池的服务 启动的时候会自动生成这个bean，生成dynamicThreadPollService
    * 通过applicationContext拿到应用的信息，会自动注入
    *
    * applicationContext：配置
    * threadPoolExecutor：线程池，因为使用者不一定只使用了一个线程池，所以用map去接受所有的线程池，key就是线程池的名字
    * */
    @Bean("dynamicThreadPollService")
    public DynamicThreadPoolSercive dynamicThreadPollService(ApplicationContext applicationContext, Map<String ,ThreadPoolExecutor> threadPoolExecutorMap,RedissonClient redissonClient){
        //getEnvironment获取环境，getProperty获取配置，拿到spring.application.name这个应用的名字
        applicationName = applicationContext.getEnvironment().getProperty("spring.application.name");//获取名字

        //如果使用者没有配置应用的名字，提醒
        if(StringUtils.isBlank(applicationName)) {//使用StringUtils工具类判断
            applicationName = "缺省的";
            logger.warn("动态线程池，启动提示。SpringBoot 应用未配置 spring.application.name 无法获取到应用名称！");
        }

        /*
        * 因为线程池的配置初始化是在使用者的配置文件的，如果后续线程池发送了修改，服务重启后不会恢复到修改后的参数
        * 所有需要实时的变更配置，获取缓存数据，设置本地线程池配置
        * */
        Set<String> threadPoolKeys = threadPoolExecutorMap.keySet();
        for (String threadPoolKey : threadPoolKeys) {
            ThreadPoolConfigEntity threadPoolConfigEntity = redissonClient.<ThreadPoolConfigEntity>getBucket(RegistryEnumVO.THREAD_POOL_CONFIG_PARAMETER_LIST_KEY.getKey() + "_" + applicationName + "_" + threadPoolKey).get();
            if (null == threadPoolConfigEntity) continue;
            ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(threadPoolKey);
            threadPoolExecutor.setCorePoolSize(threadPoolConfigEntity.getCorePoolSize());
            threadPoolExecutor.setMaximumPoolSize(threadPoolConfigEntity.getMaximumPoolSize());
        }


        //记录所有线程池的名字
        logger.info("线程池信息：{}", JSON.toJSONString(threadPoolExecutorMap.keySet()));

        return new DynamicThreadPoolSercive(applicationName,threadPoolExecutorMap);
    }

    //线程池数据上报任务对象
    @Bean
    public ThreadPoolDataReportJob threadPoolDataReportJob(IDynamicThreadPoolService dynamicThreadPoolService, IRegistry registry){
        return new ThreadPoolDataReportJob(dynamicThreadPoolService,registry);
    }

    @Bean(name = "ydnamicThreadPoolRedisTopic")
    public RTopic threadPollConfigAdjustListener(RedissonClient redissonClient, ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener){
        RTopic topic = redissonClient.getTopic(RegistryEnumVO.DYNAMIC_THREAD_POOL_REDIS_TOPIC.getKey() + "_" + applicationName);
        topic.addListener(ThreadPoolConfigEntity.class,threadPoolConfigAdjustListener);
        return topic;
    }

}
